﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Builder;
using Volo.Abp.Caching;
using Microsoft.Extensions.Caching.Distributed;
using Itech_Run.Sys.Application.Contracts.Service;
using Itech_Run.Sys.Application.Contracts.Dto;
using Itech_Run.HttpApi.Shared.Result;
using System.Text.Json;
using Itech_Run.Helper.Attribute;
using Itech_Run.HttpApi.Shared.Options;
using Microsoft.Extensions.Options;

namespace Itech_Run.Sys.HttpApi.Middleware
{
    public class ButtonAuthMiddleware : BaseMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ISysRoleModuleButtonService _sysRoleModuleButtonService;
        private readonly IDistributedCache<Dictionary<string, List<ButtonAuthDto>>, string> _buttonCache;
        private readonly ISysModuleService _sysModuleService;
        private Func<string, string, string, object[]?> _btnAttributeDelegate;
        private readonly IOptions<CacheOptions>? _cacheOptions;

        public ButtonAuthMiddleware(RequestDelegate next, IApplicationBuilder app, Func<string, string, string, object[]?> btnAttributeDelegate, ISysModuleService sysModuleService) : base(app)
        {
            _next = next;
            _btnAttributeDelegate = btnAttributeDelegate;
            _buttonCache = (IDistributedCache<Dictionary<string, List<ButtonAuthDto>>, string>)app.ApplicationServices.GetService(typeof(IDistributedCache<Dictionary<string, List<ButtonAuthDto>>, string>));
            _sysRoleModuleButtonService = (ISysRoleModuleButtonService)app.ApplicationServices.GetService(typeof(ISysRoleModuleButtonService));
            _sysModuleService = sysModuleService;
            _cacheOptions = (IOptions<CacheOptions>?)app.ApplicationServices.GetService(typeof(IOptions<CacheOptions>));
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.User.Identity != null && context.User.Identity.Name != null)
            {
                var operateUser = context.User.Identity.Name;
                //过滤的Controller->Buttons集，实际基于用户角色取对应的模块的Controller&Button，从DB取
                var buttonAuthDic = await _buttonCache.GetOrAddAsync(string.Concat("Button", "_", operateUser),
                         async () => await _sysRoleModuleButtonService.GetUserRoleModuleButtonAuthAsync(operateUser ?? ""),
                         () =>
                         {
                             return new DistributedCacheEntryOptions
                             {
                                 AbsoluteExpiration = DateTime.Now.AddSeconds(_cacheOptions?.Value.ButtonAuthUserTime ?? 0)
                             };
                         });
                if (!await CurrentActionIsAuth(buttonAuthDic, context))
                {
                    context.Response.ContentType = "application/json; charset=utf-8 ";
                    var stream = context.Response.Body;
                    await JsonSerializer.SerializeAsync(stream, new CustomResult { Code = CustomResultCode.无操作权限.To<int>().ToString(), Message = "无操作权限" }
                    , new JsonSerializerOptions
                    {
                        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                    });
                    return;
                }
            }
            await _next(context);
        }

        /// <summary>
        /// 判断当前的Action是否授权
        /// </summary>
        /// <param name="buttonAuthDic"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task<bool> CurrentActionIsAuth(Dictionary<string, List<ButtonAuthDto>> buttonAuthDic, HttpContext context)
        {
            var path = context.Request.Path.Value; // xxx/SysRole/SayAbp --需要考虑异步Action，如【SayAbp=》SayAbpAsync】
            var splitArr = path?.Split('/');
            if (splitArr?.Length >= 2)
            {
                var controller = splitArr[splitArr.Length - 2];
                controller = GetActualControllerName(controller);
                var action = splitArr[splitArr.Length - 1];
                var assemblyName = await GetAssemblyName(_sysModuleService, controller);
                //if (buttonAuthDic.ContainsKey(controller))
                {
                    var customAttributes = _btnAttributeDelegate(assemblyName ?? "", controller + "Controller", action);
                    if (customAttributes is not null)
                        for (int i = 0; i < customAttributes.Length; i++)
                        {
                            var item = customAttributes[i];
                            if (item is ButtonAuthAttribute)
                            {
                                if (buttonAuthDic.ContainsKey(controller))
                                {
                                    var buttonAuthDtoList = buttonAuthDic[controller];
                                    var attributePropertyName = ((ButtonAuthAttribute)item).GetName;
                                    if (buttonAuthDtoList.Any(buttonAuthDto => buttonAuthDto.ButtonCode == attributePropertyName && buttonAuthDto.HasAuth == true))
                                    {
                                        return true;
                                    }
                                }
                                return false;
                            }
                        }
                }
            }
            return true; //未加ButtonAuth特性的一律放行
        }
    }
}
